package com.ly.heart.service.impl;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ly.common.PageParam;
import com.ly.common.core.domain.AjaxResult;
import com.ly.common.core.domain.entity.SysDept;
import com.ly.common.exception.CustomException;
import com.ly.common.utils.DateUtils;
import com.ly.common.utils.SecurityUtils;
import com.ly.common.utils.StringUtils;
import com.ly.common.utils.erp.BatchNumberGenerator;
import com.ly.common.utils.erp.ConstUtil;
import com.ly.common.utils.erp.SnowflakeIdWorker;
import com.ly.heart.domain.*;
import com.ly.heart.domain.dto.ContractSaleOrderDto;
import com.ly.heart.domain.dto.StartProcessDto;
import com.ly.common.core.domain.ActCurrentTaskVo;
import com.ly.heart.domain.vo.ContractOrderMaterialDetailVo;
import com.ly.heart.domain.vo.ContractSaleOrderVo;
import com.ly.heart.mapper.ContractOrderMapper;
import com.ly.heart.mapper.MaterialDetailMapper;
import com.ly.heart.service.*;
import com.ly.heart.util.ChineseToPinyin;
import com.ly.heart.util.CurrentUserUtil;
import com.ly.system.service.ISysDeptService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import com.ly.heart.mapper.ContractSaleMapper;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;

/**
 * 光伏材料/逆变器/组件销售合同Service业务层处理
 *
 * @author cxl
 * @date 2024-05-16
 */
@Service
@Slf4j
public class ContractSaleServiceImpl extends ServiceImpl<ContractSaleMapper, ContractSaleEntity> implements IContractSaleService {

    @Resource
    private ContractSaleMapper contractSaleMapper;
    @Resource
    private ContractOrderMapper contractOrderMapper;
    @Resource
    private MaterialDetailMapper materialDetailMapper;
    @Resource
    private IFileInfoService iFileInfoService;
    @Resource
    private ISysDeptService deptService;
    @Resource
    private IActModelService iActModelService;
    @Resource
    private IActTaskCommentService iActTaskCommentService;
    @Resource
    private IKingBaseCurrencyService iKingBaseCurrencyService;

    /**
     * 查询光伏材料/逆变器/组件销售合同
     *
     * @param id 光伏材料/逆变器/组件销售合同主键
     * @return 光伏材料/逆变器/组件销售合同
     */
    @Override
    public ContractSaleOrderVo selectContractSaleEntityById ( String id ) {
        ContractSaleOrderVo vo = new ContractSaleOrderVo ();
        ContractSaleEntity contractSaleEntity = contractSaleMapper.selectContractSaleEntityById ( id );
        contractSaleEntity.setIsCloseName ( contractSaleEntity.getIsClose () == 1 ? "正常" : "关闭" );
        contractSaleEntity.setIsCancelName ( contractSaleEntity.getIsCancel () == 1 ? "正常" : "作废" );
        //查询物料明细
        MaterialDetailEntity materialDetailEntity = new MaterialDetailEntity ();
        materialDetailEntity.setRelationId ( id );
        List<MaterialDetailEntity> materialDetailEntities = materialDetailMapper.selectOrderWarehouseDetailEntityList ( materialDetailEntity );
        materialDetailEntities.forEach ( detail -> {
            detail.setSettlementDeptName ( contractSaleEntity.getSaleCompanyName () );
        } );
        vo.setContractSaleEntity ( contractSaleEntity ).setMaterialDetailEntities ( materialDetailEntities );
        return vo;
    }

    /**
     * 查询光伏材料/逆变器/组件销售合同列表
     *
     * @param contractSaleEntity 光伏材料/逆变器/组件销售合同
     * @return 光伏材料/逆变器/组件销售合同
     */
    @Override
    public AjaxResult selectContractSaleEntityList ( ContractSaleEntity contractSaleEntity ) {
        contractSaleEntity.setIsUsed ( ConstUtil.IS_USED_YES );
        contractSaleEntity.setPage ( ( contractSaleEntity.getPage () - 1 ) * contractSaleEntity.getLimit () );
        List<ContractSaleEntity> contractSaleEntities = contractSaleMapper.selectContractSaleEntityList ( contractSaleEntity );
        contractSaleEntities.forEach ( sale -> {
            ActTaskCommentEntity actTaskCommentEntity = iActTaskCommentService.queryLastComment ( sale.getInstanceId () );
            if ( actTaskCommentEntity != null ) {
                sale.setApprovedUser ( actTaskCommentEntity.getApprovedUser () );
                sale.setApprovedRemark ( actTaskCommentEntity.getRemark () );
            }
            //任务信息
            ActCurrentTaskVo currentTaskVo = iActModelService.getCurrentTaskByInstanceId ( sale.getInstanceId () );
            Optional.ofNullable ( currentTaskVo ).ifPresent ( currentTask -> {
                sale.setCurrentTask ( currentTask );
            } );
            sale.setIsCloseName ( sale.getIsClose () == 1 ? "正常" : "关闭" );
            sale.setIsCancelName ( sale.getIsCancel () == 1 ? "正常" : "作废" );
        } );
        Integer integer = contractSaleMapper.queryCount ( contractSaleEntity );
        PageParam page = new PageParam ();
        page.setCurrent ( contractSaleEntity.getPage () ).setRecords ( contractSaleEntities ).setTotal ( integer );
        return AjaxResult.success ( page );
    }

    /**
     * 新增光伏材料/逆变器/组件销售合同
     * 1、处理销售合同基础数据
     * 2、处理销售订单列表
     * 3、处理销售物料明细列表
     *
     * @param contractSaleOrderDto 光伏材料/逆变器/组件销售合同
     * @return 结果
     */
    @Override
    @Transactional ( rollbackFor = Exception.class, propagation = Propagation.REQUIRED )
    public AjaxResult draft ( ContractSaleOrderDto contractSaleOrderDto ) {
        ContractSaleEntity contractSaleEntity = contractSaleOrderDto.getContractSaleEntity ();
        contractSaleEntity.setId ( SnowflakeIdWorker.getInstance ().nextStringId () );
        if ( StringUtils.isNoneBlank ( contractSaleEntity.getParentId () ) ) {
            //说明走变更，根据父编号生成编号：老编号_001
            ContractSaleEntity parentContract = baseMapper.selectById ( contractSaleEntity.getParentId () );
            String contractNumber = parentContract.getContractNumber ();
            int count = this.count ( new QueryWrapper<ContractSaleEntity> ().eq ( "parent_id", contractSaleEntity.getParentId () ) );
            String countStr = String.format ( "%02d", count );
            contractSaleEntity.setContractNumber ( contractNumber.concat ( "_" ).concat ( countStr ) );
            contractSaleEntity.setIsChange ( ConstUtil.CONTRACT_IS_CHANGE_YES );
            //检查能够变更销售订单
            checkNumber ( contractSaleOrderDto.getMaterialDetailEntities () );
        } else {
            SysDept sysDept = deptService.getById ( CurrentUserUtil.getCompanyId () );
            String firstLetter = ChineseToPinyin.getFirstLetter ( sysDept.getDeptName () );
            String nowDateString = new SimpleDateFormat ( "yyyy-MM-dd" ).format ( new Date () );
            int count = this.count ( new QueryWrapper<ContractSaleEntity> ().eq ( "type", contractSaleEntity.getType () ).eq ( "dept_id", contractSaleEntity.getDeptId () ) );
            String countStr = String.format ( "%04d", count );
            String contractNumber = firstLetter.concat ( "_" ).concat ( contractSaleEntity.getType () == ConstUtil.CONTRACT_PROCURE_TYPE ? "采购合同" : "行政采购" ).concat ( "_" ).concat ( nowDateString ).concat ( countStr );
            contractSaleEntity.setContractNumber ( contractNumber );
        }
        contractSaleEntity.setCreateBy ( SecurityUtils.getLoginUser ().getUser ().getUserId () );
        contractSaleEntity.setCreateTime ( DateUtils.getNowDate () );
        //2、处理合同附件
        List<FileEntity> fileEntities = contractSaleEntity.getFileEntities ();
        updateFile ( fileEntities, contractSaleEntity.getId () );
        //4、处理采购物料
        List<MaterialDetailEntity> materialDetailEntities = contractSaleOrderDto.getMaterialDetailEntities ();
        int updateMaterial = updateMaterial ( materialDetailEntities, contractSaleEntity.getId () );
        if ( updateMaterial == 0 ) {
            return AjaxResult.error ( "新增销售合同与物料明细关联关系失败" );
        }
        contractSaleEntity.setDeptId ( CurrentUserUtil.getCompanyId () );
        contractSaleEntity.setState ( ConstUtil.STATE_IN_DRAFT );
        boolean save = this.save ( contractSaleEntity );
        if ( ! save ) {
            return AjaxResult.error ( "暂存销售合同失败" );
        }
        return AjaxResult.success ( "暂存销售合同失败", contractSaleEntity.getId () );
    }


    @Override
    @Transactional ( rollbackFor = Exception.class, propagation = Propagation.REQUIRED )
    public AjaxResult submit ( ContractSaleEntity contractSaleEntity ) {
        if ( StringUtils.isBlank ( contractSaleEntity.getId () ) ) {
            return AjaxResult.error ( "请先暂存该单据！" );
        }
        //处理审批流
        StartProcessDto startDto = new StartProcessDto ().
                setBusinessId ( contractSaleEntity.getId () ).
                setBusinessType ( contractSaleEntity.getBusinessType () ).setModelId ( contractSaleEntity.getModelId () ).
                setBusinessName ( "销售合同_".concat ( contractSaleEntity.getId () ) );
        String instanceId = iActModelService.startProcess ( startDto );
        boolean update = this.update ( new LambdaUpdateWrapper<ContractSaleEntity> ().
                set ( ContractSaleEntity :: getInstanceId, instanceId ).
                set ( ContractSaleEntity :: getState, ConstUtil.STATE_IN_PROCESS ).eq ( ContractSaleEntity :: getId, contractSaleEntity.getId () ) );
        if ( ! update ) {
            throw new CustomException ( "提交销售合同失败！" );
        }
        //处理基础数据绑定关系
        ContractSaleEntity saleEntity = this.getById ( contractSaleEntity );
        boolean bindCurrency = iKingBaseCurrencyService.update ( new LambdaUpdateWrapper<KingBaseCurrencyEntity> ().
                set ( KingBaseCurrencyEntity :: getIsDel, 2 ).
                set ( KingBaseCurrencyEntity :: getUpdateBy, SecurityUtils.getLoginUser ().getUser ().getUserId () ).
                set ( KingBaseCurrencyEntity :: getUpdateTime, DateUtils.getNowDate () ).
                eq ( KingBaseCurrencyEntity :: getId, saleEntity.getSettlementCurrencyId () ) );
        if ( ! bindCurrency ) {
            throw new CustomException ( "更新基础数据引用关系失败！" );
        }
        return AjaxResult.success ( "提交销售合同成功" );
    }

    /**
     * 修改光伏材料/逆变器/组件销售合同
     *
     * @param contractSaleOrderDto 光伏材料/逆变器/组件销售合同
     * @return 结果
     */
    @Override
    @Transactional ( rollbackFor = Exception.class, propagation = Propagation.REQUIRED )
    public AjaxResult updateContractSaleEntity ( ContractSaleOrderDto contractSaleOrderDto ) {
        ContractSaleEntity contractSaleEntity = contractSaleOrderDto.getContractSaleEntity ();
        ContractSaleEntity sourceContract = this.getById ( contractSaleEntity );
        if ( sourceContract.getState ().equals ( ConstUtil.STATE_FINISH ) ) {
            return AjaxResult.error ( "审批完成的单据无法操作！" );
        }
        contractSaleEntity.setChangeTime ( DateUtils.getNowDate () );
        contractSaleEntity.setUpdateBy ( SecurityUtils.getLoginUser ().getUser ().getUserId () );
        boolean b = this.updateById ( contractSaleEntity );
        if ( ! b ) {
            throw new CustomException ( "修改销售合同基本信息失败！" );
        }
        //4、处理采购物料
        List<MaterialDetailEntity> materialDetailEntities = contractSaleOrderDto.getMaterialDetailEntities ();
        int updateMaterial = updateMaterial ( materialDetailEntities, contractSaleEntity.getId () );
        if ( updateMaterial == 0 ) {
            return AjaxResult.error ( "新增采销售合同与物料明细关联关系失败" );
        }
        return AjaxResult.success ();
    }

    /**
     * 批量删除光伏材料/逆变器/组件销售合同
     *
     * @param ids 需要删除的光伏材料/逆变器/组件销售合同主键
     * @return 结果
     */
    @Override
    @Transactional ( rollbackFor = Exception.class, propagation = Propagation.REQUIRED )
    public AjaxResult deleteContractSaleEntityByIds ( String[] ids ) {
        LambdaUpdateWrapper<ContractSaleEntity> contractUpdate = new LambdaUpdateWrapper<> ();
        contractUpdate.set ( ContractSaleEntity :: getIsUsed, ConstUtil.IS_USED_NO ).in ( ContractSaleEntity :: getId, ids );
        boolean update = this.update ( contractUpdate );
        if ( ! update ) {
            throw new CustomException ( "删除销售合同失败！" );
        }
        LambdaUpdateWrapper<MaterialDetailEntity> materialUpdate = new LambdaUpdateWrapper<> ();
        materialUpdate.set ( MaterialDetailEntity :: getIsUsed, ConstUtil.IS_USED_NO ).in ( MaterialDetailEntity :: getRelationId, ids );
        int updateDetail = materialDetailMapper.update ( null, materialUpdate );
        if ( updateDetail == 0 ) {
            throw new CustomException ( "删除销售合同失败！" );
        }
        return AjaxResult.success ( "删除销售合同成功" );
    }

    /**
     * 通过选单销售合同，查询销售合同的所有物料
     *
     * @return
     */
    @Override
    public AjaxResult queryMaterial ( ContractSaleEntity contractSaleEntity ) {
        contractSaleEntity.setPage ( ( contractSaleEntity.getPage () - 1 ) * contractSaleEntity.getLimit () );
        List<ContractOrderMaterialDetailVo> contractOrderMaterialDetailVos = contractSaleMapper.queryMaterial ( contractSaleEntity );
        Integer integer = contractSaleMapper.queryCountMenu ( contractSaleEntity );
        PageParam pageParam = new PageParam ();
        pageParam.setRecords ( contractOrderMaterialDetailVos ).setTotal ( integer );
        return AjaxResult.success ( pageParam );
    }

    /**
     * 关闭销售合同
     *
     * @param id
     * @return
     */
    @Override
    public AjaxResult close ( String id ) {
        ContractSaleEntity saleEntity = this.getById ( id );
        if ( saleEntity == null ) {
            return AjaxResult.error ( "无此合同！" );
        }
        boolean update = this.update ( new LambdaUpdateWrapper<ContractSaleEntity> ().
                set ( ContractSaleEntity :: getIsClose, saleEntity.getIsClose () == 1 ? 2 : 1 ).
                set ( ContractSaleEntity :: getUpdateBy, SecurityUtils.getLoginUser ().getUser ().getUserId () ).
                set ( ContractSaleEntity :: getChangeTime, DateUtils.getNowDate () ).
                eq ( ContractSaleEntity :: getId, id ) );
        if ( ! update ) {
            throw new CustomException ( saleEntity.getIsClose () == 1 ? "关闭销售合同失败！" : "反关闭销售合同失败！" );
        }
        return AjaxResult.success ();
    }

    @Override
    public AjaxResult cancel ( String id ) {
        ContractSaleEntity saleEntity = this.getById ( id );
        if ( saleEntity == null ) {
            return AjaxResult.error ( "无此合同！" );
        }
        boolean update = this.update ( new LambdaUpdateWrapper<ContractSaleEntity> ().
                set ( ContractSaleEntity :: getIsCancel, 2 ).
                set ( ContractSaleEntity :: getUpdateBy, SecurityUtils.getLoginUser ().getUser ().getUserId () ).
                set ( ContractSaleEntity :: getChangeTime, DateUtils.getNowDate () ).
                eq ( ContractSaleEntity :: getId, id ) );
        if ( ! update ) {
            throw new CustomException ( "作废销售合同失败！" );
        }
        return AjaxResult.success ();
    }

    /**
     * 删除光伏材料/逆变器/组件销售合同信息
     *
     * @param id 光伏材料/逆变器/组件销售合同主键
     * @return 结果
     */
    @Override
    public int deleteContractSaleEntityById ( String id ) {
        return contractSaleMapper.deleteContractSaleEntityById ( id );
    }


    /**
     * 修改勘测基础模块的附件，更新relationId为勘测记录Id
     *
     * @param fileEntities
     * @param relationId
     */
    private void updateFile ( List<FileEntity> fileEntities, String relationId ) {
        if ( fileEntities != null && fileEntities.size () > 0 ) {
            fileEntities.forEach ( fileEntity -> {
                fileEntity.setRelationId ( relationId );
                fileEntity.setChangeTime ( DateUtils.getNowDate () );
            } );
            boolean b = iFileInfoService.updateBatchById ( fileEntities );
            if ( ! b ) {
                throw new RuntimeException ( "修改销售合同附件失败" );
            }
        }
    }

    /**
     * 处理采购合同与物料明细关联关系
     * 先删除原有关系
     * 再新增现有关系
     *
     * @param materialDetailEntities
     * @param contractId
     * @return
     */
    private int updateMaterial ( List<MaterialDetailEntity> materialDetailEntities, String contractId ) {
        if ( materialDetailEntities != null && materialDetailEntities.size () > 0 ) {
            String batchNumber = BatchNumberGenerator.generateBatchNumber ();
            //1.删除销售订单关系
            //1.先删除
            contractOrderMapper.delete ( new QueryWrapper<ContractOrderEntity> ().eq ( "contract_id", contractId ).eq ( "source_contract_type", ConstUtil.CONTRACT_SALE_TYPE ) );
            List<String> ids = materialDetailEntities.stream ().map ( MaterialDetailEntity :: getRelationId ).collect ( Collectors.toList () ).stream ().distinct ().collect ( Collectors.toList () );
            ids.forEach ( id -> {
                ContractOrderEntity contractOrderEntity = new ContractOrderEntity ();
                contractOrderEntity.setId ( SnowflakeIdWorker.getInstance ().nextStringId () );
                contractOrderEntity.setContractId ( contractId );
                contractOrderEntity.setSourceContractType ( ConstUtil.CONTRACT_SALE_TYPE );
                contractOrderEntity.setOrderId ( id );
                contractOrderEntity.setSourceOrderType ( ConstUtil.ORDER_SALE_TYPE );
                contractOrderEntity.setCreateBy ( SecurityUtils.getLoginUser ().getUser ().getUserId () );
                contractOrderEntity.setCreateTime ( DateUtils.getNowDate () );
                int insert = contractOrderMapper.insert ( contractOrderEntity );
                if ( insert == 0 ) {
                    throw new RuntimeException ( "新增销售合同与销售订单关联关系失败" );
                }
            } );
            materialDetailMapper.delete ( new QueryWrapper<MaterialDetailEntity> ().eq ( "relation_id", contractId ).eq ( "type", ConstUtil.CONTRACT_SALE_MATERIAL_TYPE ) );
            //新增现有关系
            materialDetailEntities.forEach ( materialDetailEntity -> {
                materialDetailEntity.setId ( SnowflakeIdWorker.getInstance ().nextStringId () );
                materialDetailEntity.setRelationId ( contractId );
                materialDetailEntity.setBatchNum ( batchNumber );
                materialDetailEntity.setType ( ConstUtil.CONTRACT_SALE_MATERIAL_TYPE );
                materialDetailEntity.setCreateTime ( DateUtils.getNowDate () );
                int insert = materialDetailMapper.insert ( materialDetailEntity );
                if ( insert == 0 ) {
                    throw new RuntimeException ( "新增销售合同与物料明细关联关系失败" );
                }
            } );
        }
        return 1;
    }

    /**
     * 采购合同变更时，检查合同物料明细的数量是否超过采购订单里已执行完成的物料数量
     * 如果超过则不给变更成功
     *
     * @param materialDetailEntities：新增采购合同时选择的物料
     */
    private void checkNumber ( List<MaterialDetailEntity> materialDetailEntities ) {
        //拿该采购订单的物料列表；要看目前物料列表的每条数量是不是小于已生成采购订单里物料的数量
        if ( ! CollectionUtils.isEmpty ( materialDetailEntities ) ) {
            materialDetailEntities = materialDetailEntities.stream ().filter ( detail -> StringUtils.isNotBlank ( detail.getSourceMaterialDetailId () ) ).collect( Collectors.toList());
            List<String> ids = materialDetailEntities.stream ().map ( MaterialDetailEntity :: getRelationId ).collect ( Collectors.toList () ).stream ().distinct ().collect ( Collectors.toList () );
            List<MaterialDetailEntity> detailEntities = materialDetailMapper.selectList ( new QueryWrapper<MaterialDetailEntity> ().eq ( "type", ConstUtil.ORDER_SALE ).in ( "relation_id", ids ) );
            log.info ( "已生成销售订单的物料列表：detailEntities->{}", detailEntities );
            //1、已生成销售订单的物料，在做合同变更时不能删除，detailEntities里面的物料不能少于materialDetailEntities物料
            Set<String> materialByOrder = detailEntities.stream ().map ( MaterialDetailEntity :: getId ).collect ( Collectors.toSet () );
            Set<String> materialByContract = materialDetailEntities.stream ().map ( MaterialDetailEntity :: getId ).collect ( Collectors.toSet () );
            //检查已生成订单的物料Id是否都在新增合同的物料Id中
            boolean allExist = materialByOrder.stream ().allMatch ( materialByContract :: contains );
            if ( ! allExist ) {
                throw new RuntimeException ( "已生成销售订单的物料不能删除！" );
            }
            //2、比较detailEntities和新增的materialDetailEntities的数量，materialDetailEntities里面每一条物料数量不得小于detailEntities里每一条物料数量；
            for ( int i = 0; i < detailEntities.size (); i++ ) {
                MaterialDetailEntity materByOrder = detailEntities.get ( i );
                MaterialDetailEntity materByContract = materialDetailEntities.get ( i );
                if ( Double.parseDouble ( materByOrder.getNumber () ) > Double.parseDouble ( materByContract.getNumber () ) ) {
                    throw new RuntimeException ( "变更后的物料数量不能少于已生成销售订单的物料数量！" );
                }
            }
        }
    }

}
